package com.ldxiwxr.system.service.impl;

import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.secure.BCrypt;
import cn.dev33.satoken.secure.SaSecureUtil;
import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ldxiwxr.common.common.MapResult;
import com.ldxiwxr.common.common.Result;
import com.ldxiwxr.common.enums.DeviceType;
import com.ldxiwxr.common.utils.*;
import com.ldxiwxr.system.dto.UserDto;
import com.ldxiwxr.system.entity.Role;
import com.ldxiwxr.system.entity.User;
import com.ldxiwxr.system.entity.UserInfo;
import com.ldxiwxr.system.entity.UserRole;
import com.ldxiwxr.system.mapper.*;
import com.ldxiwxr.system.service.IUserService;
import com.ldxiwxr.system.utils.HandleUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import static com.ldxiwxr.common.constant.MsgTypeConstant.LOGIN_MSG;
import static com.ldxiwxr.common.constant.MsgTypeConstant.REGISTER_MSG;
import static com.ldxiwxr.common.constant.RedisConstant.*;
import static com.ldxiwxr.common.constant.UserPermissionConstant.NORMAL_PERMISSION;
import static com.ldxiwxr.common.constant.UserStatus.ENABLE_USER;
import static com.ldxiwxr.system.common.Constant.LOGIN_USER;

/**
 * UserService 实现类
 * @author SuBanxia
 */
@Service
@RequiredArgsConstructor
public class UserServiceImpl implements IUserService {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private UserMapper userMapper;

    @Resource
    private RandomMsgUtils randomMsgUtils;

    @Autowired
    private TXSDKUtils txsdkUtils;

    @Resource
    private UserInfoMapper userInfoMapper;

    @Resource
    private UserRoleMapper userRoleMapper;

    @Resource
    private RoleMapper roleMapper;

    @Resource
    private MenuMapper menuMapper;

    /**
     * 登录实现
     * @param username 用户名
     * @param password 密码
     * @param code 验证码
     * @param request 请求
     */
    @Override
    public Result<?> login(String username, String password, String code, Boolean isRem, HttpServletRequest request) {

        //判断验证码是否正确无误
        String redisCode = (String) redisTemplate.opsForValue().get(CODE);
        if (!code.equalsIgnoreCase(redisCode)){
            return Result.fail("验证码错误");
        }
        //查询用户密码
        User user = userMapper.selectOne(Wrappers.<User>lambdaQuery().eq(User::getUsername, username));
        //判断密码是否正确
        if (!user.getPassword().equals(SaSecureUtil.md5(password))) {
            return Result.fail("用户名或密码错误");
        }
        //将用户信息存入仓库中
        SaHolder.getStorage().set(LOGIN_USER, user);
        //会话登录，PC端
        SaLoginModel loginModel = SaLoginModel.create().setDevice(DeviceType.PC.getDevice()).setExtra(LOGIN_USER, user).setIsLastingCookie(isRem);
        StpUtil.login(user.getId(), loginModel);
        //开启SaToke的session
        StpUtil.getSession().set(LOGIN_USER, user);
        //改变用户相关登录信息
        updateUserLoginInfo(user);
        //返回SaToken的Token
        return Result.data(StpUtil.getTokenValue());
    }

    /**
     * 改变用户相关登录信息
     * @param user 用户
     */
    @Override
    public void updateUserLoginInfo(User user) {
        //设置ip地址
        user.setIpAddress(ServletUtils.getClientIP());
        //设置最后登录时间
        user.setLastLoginTime(DateUtils.getNowDate());
        //设置ip属地
        user.setIpSource(IPUtils.getAddressByIp(ServletUtils.getClientIP()));
        //设置用户更新时间
        user.setUpdateTime(DateUtils.getNowDate());
        //更新
        userMapper.updateById(user);
    }

    /**
     * 忘记密码采用绑定手机登录，并更改密码功能实现
     * @param phoneNumber 登陆手机号
     * @param msgCode 短信验证码
     * @param newPassword 新密码
     */
    @Override
    public Result<?> loginByForgetPwd(String phoneNumber, String msgCode, String newPassword) {
        //从redis中获取短信验证码内容
        String code = (String) redisTemplate.opsForValue().get(REDIS_LOGIN_MSG);
        //判断code的值是否和验证码内容相同
        if (!code.equals(msgCode)){
            return Result.fail("短信验证码错误");
        }
        //通过查询电话号码对印的用户信息，查出用户id，然后获取到用户
        UserInfo userInfo = userInfoMapper.selectOne(Wrappers.<UserInfo>lambdaQuery().eq(UserInfo::getPhone, phoneNumber));
        User user = userMapper.selectById(userInfo.getId());
        //将用户信息存入仓库中
        SaHolder.getStorage().set(LOGIN_USER, user);
        //会话登录，手机端
        SaLoginModel loginModel = SaLoginModel.create().setDevice(DeviceType.APP.getDevice()).setExtra(LOGIN_USER, user);
        StpUtil.login(user.getId(), loginModel);
        //开启SaToke的session
        StpUtil.getSession().set(LOGIN_USER, user);
        //改变用户相关登录信息
        updateUserLoginInfo(user);
        //修改该用户密码
        user.setPassword(SaSecureUtil.md5(newPassword));
        userMapper.updateById(user);
        //返回SaToken的Token
        return Result.success(StpUtil.getTokenValue());
    }


    /**
     * 发送信息
     * @param phoneNumber 电话号码
     * @param msgType 信息类型
     */
    @Override
    public void sendMsg(String phoneNumber, String msgType) {
        String msg = randomMsgUtils.getRandomNumber();
        //采用switch case语句判断是发送哪一种消息
        switch (msgType){
            //注册消息保存在Redis中
            case REGISTER_MSG :
                redisTemplate.opsForValue().set(REDIS_REGISTER_MSG, msg);
                break;
            //登陆消息保存在Redis中
            case LOGIN_MSG :
                redisTemplate.opsForValue().set(REDIS_LOGIN_MSG, msg);
                break;
            default : break;
        }
        txsdkUtils.sendMsg(msgType, msg, phoneNumber);
    }

    /**
     * 登出功能
     * @return Result结果
     */
    @Override
    public Result<?> logout() {
        //获取登录用户
        User user = (User) StpUtil.getSession().get(LOGIN_USER);
        //退出登录
        StpUtil.logout(user.getId());
        return Result.success("登出成功");
    }

    /**
     * 获取用户信息
     * @return User 用户
     */
    @Override
    public User getUser() {
        //直接返回登陆用户信息
        User user = (User) StpUtil.getSession().get(LOGIN_USER);
        //定义wrapper查询role
        LambdaQueryWrapper<UserRole> wrapper = Wrappers.<UserRole>lambdaQuery().eq(UserRole::getUserId, user.getId());
        UserRole userRole = userRoleMapper.selectOne(wrapper);
        //给用户role赋值
        Role role = roleMapper.selectById(userRole.getRoleId());
        user.setRole(role);
        //获取具体权限
        List<Integer> permissions = HandleUtils.getPermissionByString(role.getPermission());
        user.setPermission(menuMapper.selectPermission(permissions));
        return user;
    }

    /**
     * 注册功能
     * @param userName 用户名
     * @param phoneNumber 电话号码
     * @param msgCode 验证码
     * @param password 密码
     * @return Result 结果
     */
    @Override
    public Result<?> register(String userName, String phoneNumber, String msgCode, String password) {
        //从redis中获取短信验证码内容
        String code = (String) redisTemplate.opsForValue().get(REDIS_REGISTER_MSG);
        //判断发送的验证码和输入的是否相符
        if (!code.equals(msgCode)){
            return Result.fail("短信验证码错误");
        }
        //创建一个User对象并给他赋值
        User user = new User();
        //设置用户名
        user.setUsername(userName);
        //设置密码
        user.setPassword(SaSecureUtil.md5(password));
        //添加user
        addUser(user);
        //创建UserInfo并给他赋值
        UserInfo userInfo = new UserInfo();
        //获取user的id赋值给userInfo的id
        LambdaQueryWrapper<User> wrapper = Wrappers.<User>lambdaQuery().eq(User::getUsername, userName);
        userInfo.setId(userMapper.selectOne(wrapper).getId());
        //设置电话号码
        userInfo.setPhone(phoneNumber);
        //添加userInfo
        addUserInfo(userInfo);
        //创建用户权限
        UserRole userRole = new UserRole();
        //获取用户id并赋值
        userRole.setUserId(userMapper.selectOne(wrapper).getId());
        //用户权限赋值
        userRole.setRoleId(NORMAL_PERMISSION);
        userRoleMapper.insert(userRole);
        return Result.success("用户创建成功");
    }

    /**
     * 添加用户
     * @param user 用户类
     */
    @Override
    public void addUser(User user) {
        //给User对象赋值
        //设置ip地址
        user.setIpAddress(ServletUtils.getClientIP());
        //设置ip属地
        user.setIpSource(IPUtils.getAddressByIp(ServletUtils.getClientIP()));
        //设置最后登录时间
        user.setLastLoginTime(DateUtils.getNowDate());
        //设置用户可用
        user.setEnabled(true);
        //设置用户创建时间
        user.setCreateTime(DateUtils.getNowDate());
        //设置用户更新时间
        user.setUpdateTime(DateUtils.getNowDate());
        //user对象写入数据库
        userMapper.insert(user);
    }


    /**
     * 添加用户信息
     * @param userInfo 用户信息
     */
    @Override
    public void addUserInfo(UserInfo userInfo) {
        //userInfo对象写入数据库
        userInfoMapper.insert(userInfo);
    }

    /**
     * 修改用户头像
     * @param avatarFileName 头像文件名
     */
    @Override
    public void updateUserAvatar(String avatarFileName) {
        //获取用户对象
        User user = (User) StpUtil.getSession().get(LOGIN_USER);
        //给对象对应的头像内容赋值
        user.setAvatar(avatarFileName);
        //将修改填入数据库
        userMapper.updateById(user);
    }

    @Override
    public List<UserDto> getAllUser(String nickName, Integer id) {
        LambdaQueryWrapper<User> wrapper = Wrappers.<User>lambdaQuery().like(nickName != null, User::getNickName, nickName).eq(id != null, User::getId, id);
        List<User> users = userMapper.selectList(wrapper);
        List<UserDto> userDtos;
        userDtos = users.stream().map((item) ->{
            UserDto userDto = new UserDto();
            BeanUtils.copyProperties(item,userDto);
            UserInfo userInfo = userInfoMapper.selectById(item.getId());
            userDto.setUserInfo(userInfo);
            return userDto;
        }).collect(Collectors.toList());
        return userDtos;
    }

    @Override
    public Result<?> getUserIp(String ip) {
        MapResult mapResult = IPUtils.getMapResult(ip);
        return Result.data(mapResult);
    }
}
